package com.nulldev.util.osAPI.hardware.gpu;

import java.util.Arrays;
import java.util.regex.Pattern;

import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.VariableAPI.MathUtil;
import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.data.FinalBypass;
import com.nulldev.util.logging.Logger;
import com.nulldev.util.logging.LoggerFactory;
import com.nulldev.util.osAPI.OS;

public class GPU {
	private final static Logger log = LoggerFactory.getLogger();

	private final Vendor vendor;
	private final String model;
	private final String[] data;

	public GPU(final Vendor vendor) {
		this.vendor = vendor;
		this.model = "Unknown Model by N/A";
		this.data = new String[]
			{ "Unknown", "N/A", "N/A", "N/A" };
	}

	public GPU(final Vendor vendor, final String model) {
		this.vendor = vendor;
		this.model = model;
		this.data = model.split(" ");
	}

	public Vendor getVendor() {
		return this.vendor;
	}

	public String getGPU() {
		return StringsUtil.filterStringS(this.model, 1);
	}

	public Brand getBrand() {
		try {
			if (this.vendor == Vendor.NVIDIA) {
				try {
					if (this.data[1].equals("GeForce") && this.data[2].equals("GTX")) {
						return Brand.GEFORCE_GTX;
					} else if (this.data[1].equals("GeForce") && MathUtil.stringBetweenList(this.data, 2, 1000, 9800)) {
						return Brand.GEFORCE;
					} else if (this.data[1].equals("GeForce")) {
						return Brand.GEFORCE_OLD;
					}
				} catch (Exception e) {
					if (this.data[1].equals("GeForce") && MathUtil.stringBetweenList(this.data, 2, 1000, 9800)) {
						return Brand.GEFORCE;
					} else if (this.data[1].equals("GeForce")) {
						return Brand.GEFORCE_OLD;
					}
				}
			} else if (this.vendor == Vendor.RADEON) {
				final boolean isRX = StringsUtil.isEqual(this.data, 1, "RX");
				final boolean isVega = StringsUtil.isEqual(this.data, 2, "Vega");
				if (isRX && !isVega) {
					return Brand.RADEON_RX;
				} else if (isRX && isVega) {
					return Brand.RADEON_RX_VEGA;
				}
			} else if (this.vendor == Vendor.AMD) {
				final boolean isR9 = StringsUtil.isEqual(this.data, 2, "R9") || StringsUtil.isEqual(data, data.length - 2, "R9");
				final boolean isR5 = StringsUtil.isEqual(this.data, 2, "R5") || StringsUtil.isEqual(data, data.length - 2, "R5");
				final boolean isR7 = StringsUtil.isEqual(this.data, 2, "R7") || StringsUtil.isEqual(data, data.length - 2, "R7");
				final boolean isR3 = StringsUtil.isEqual(this.data, 2, "R3") || StringsUtil.isEqual(data, data.length - 2, "R3");

				final boolean isHD8xxx = StringsUtil.isEqual(this.data, 7, "HD") && MathUtil.stringBetweenListRC(this.data, 8, 8000, 8999);
				final boolean isHD7xxx = StringsUtil.isEqual(this.data, 7, "HD") && MathUtil.stringBetweenListRC(this.data, 8, 7000, 7999);
				final boolean isHD6xxx = StringsUtil.isEqual(this.data, 7, "HD") && MathUtil.stringBetweenListRC(this.data, 8, 6000, 6999);
				final boolean isHD5xxx = StringsUtil.isEqual(this.data, 7, "HD") && MathUtil.stringBetweenListRC(this.data, 8, 5000, 5999);
				final boolean isHD4xxx = StringsUtil.isEqual(this.data, 7, "HD") && MathUtil.stringBetweenListRC(this.data, 8, 4000, 4999);
				final boolean isHD3xxx = StringsUtil.isEqual(this.data, 7, "HD") && MathUtil.stringBetweenListRC(this.data, 8, 3000, 3999);
				final boolean isHD2xxx = StringsUtil.isEqual(this.data, 7, "HD") && MathUtil.stringBetweenListRC(this.data, 8, 2000, 2999);
				final FinalBypass<Boolean> isXT = new FinalBypass<Boolean>(false);
				final FinalBypass<Boolean> isXT1xxx = new FinalBypass<Boolean>(false);
				try {
					final String out = this.data[2].substring(1);
					final int range = Integer.parseInt(out);
					isXT.setObject(range < 1000 && range > 0 && this.model.contains("XT"));
				} catch (Exception e) {
				}
				try {
					final int range = Integer.parseInt(this.data[2].substring(1));
					isXT1xxx.setObject(range < 2000 && range > 1000);
				} catch (Exception e) {
				}
				if (isR9) {
					return Brand.AMD_R9;
				} else if (isR7) {
					return Brand.AMD_R7;
				} else if (isR5) {
					return Brand.AMD_R5;
				} else if (isR3) {
					return Brand.AMD_R3;
				} else if (isHD8xxx) {
					return Brand.AMD_HD_8XXX;
				} else if (isHD7xxx) {
					return Brand.AMD_HD_7XXX;
				} else if (isHD6xxx) {
					return Brand.AMD_HD_6XXX;
				} else if (isHD5xxx) {
					return Brand.AMD_HD_5XXX;
				} else if (isHD4xxx) {
					return Brand.AMD_HD_4XXX;
				} else if (isHD3xxx) {
					return Brand.AMD_HD_3XXX;
				} else if (isHD2xxx) {
					return Brand.AMD_HD_2XXX;
				} else if (isXT1xxx.getObject()) {
					return Brand.AMD_HD_1XXX;
				} else if (isXT.getObject()) {
					return Brand.AMD_XT;
				} else if (this.vendor == Vendor.AMD && OS.isLinux()) {
					final String[] dta = StringsUtil
							.substringBetween(ArrayUtils.StringArrayUtils.addFrom(this.data, 0).replaceAll(Pattern.quote("[AMD/ATI]"), ""), "[", "]")
							.split(" ");
					if (dta[0].equals("Radeon") && dta[1].equals("RX")) {
						try {
							if (dta[2].equals("Vega")) {
								return Brand.RADEON_RX_VEGA;
							} else {
								return Brand.RADEON_RX;
							}
						} catch (Exception e) {
							return Brand.RADEON_RX;
						}
					}
				}
			} else if (this.vendor == Vendor.INTEL) {
				final boolean is6xx = MathUtil.stringBetweenList(this.data, 2, 600, 699);
				final boolean is5xx = MathUtil.stringBetweenList(this.data, 2, 500, 599);
				final boolean is4xx = MathUtil.stringBetweenList(this.data, 2, 500, 599);
				if (is6xx) {
					return Brand.INTEL_HD_6XX;
				} else if (is5xx) {
					return Brand.INTEL_HD_5XX;
				} else if (is4xx) {
					return Brand.INTEL_HD_4XX;
				} else if (ArrayUtils.StringArrayUtils.containsInString(this.data, "UHD")) {
					return Brand.INTEL_UHD;
				} else if (ArrayUtils.StringArrayUtils.containsInString(this.data, "HD")) {
					return Brand.INTEL_HD;
				} else {
					log.warn("Unknown brand: " + Arrays.toString(this.data));
				}
			}
		} catch (Exception e) {
			return Brand.UNKNOWN;
		}
		return Brand.UNKNOWN;
	}

	@Deprecated
	public String filteredModel() {
		if (this.vendor == Vendor.NVIDIA) {
			final boolean isTi = StringsUtil.isEqual(this.data, 4, "Ti");
			if (isTi) {

			}
		}
		return this.model;
	}

	@Override
	public String toString() {
		final StringBuilder out = new StringBuilder(24);
		out.append("GPU[" + "gpu=" + (OS.isLinux() ? ArrayUtils.StringArrayUtils.addFrom(this.data, 0) : ArrayUtils.StringArrayUtils.addFrom(this.data, 1))
				+ ",vendor=" + this.vendor + ",brand=" + this.getBrand().displayName() + "]");
		return out.toString();
	}

	public String[] getData() {
		return this.data;
	}

	public void println() {
		JVM.println(this);
	}

	public void dx_report() {
		for (int i = 1; i < 13; i++) {
			JVM.println("DX" + i + " support >> " + D3DUtil.isD3DReady(this, i));
		}
	}
}
